﻿
// CMediainfoscanner.cpp : файл реализации
//

#include "stdafx.h"
#include "mediainfo-scanner.h"
#include "CMediainfoscanner.h"
#include "afxdialogex.h"
#include "CInternetHandle.h"

//#include "SimpleXML.h"
#include "MediaInfo/MediaInfo.h"
#include "MediaInfo/MediaInfo_Config.h"
#include "ZenLib/ZtringListList.h"

#pragma comment(lib, "MediaInfo.lib")
#pragma comment(lib, "ZenLib.lib")
#pragma comment(lib, "zlib.lib")
#pragma comment(lib, "wininet.lib")

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// Диалоговое окно CAboutDlg используется для описания сведений о приложении

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// Данные диалогового окна
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // поддержка DDX/DDV

// Реализация
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// диалоговое окно CMediainfoscannerDlg




CMediainfoscannerDlg::CMediainfoscannerDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CMediainfoscannerDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMediainfoscannerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_SCANNER_PROGRESS, m_scanner_progress);
	DDX_Control(pDX, IDC_SCANNER_RESULT_LIST, m_scanner_result);
	DDX_Control(pDX, IDC_MFCSHELLTREE1, m_filetree);
	DDX_Control(pDX, IDC_SCANNER_PATH_EDIT, m_scanner_path);
	DDX_Control(pDX, IDC_START_SCANNER_BUTTON, m_start_scanner);
}

BEGIN_MESSAGE_MAP(CMediainfoscannerDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_START_SCANNER_BUTTON, &CMediainfoscannerDlg::OnBnClickedStartScannerButton)
	ON_NOTIFY(TVN_SELCHANGED, IDC_MFCSHELLTREE1, &CMediainfoscannerDlg::OnTreeSelChanged)
END_MESSAGE_MAP()


// обработчики сообщений CMediainfoscannerDlg

BOOL CMediainfoscannerDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// Добавление пункта "О программе..." в системное меню.

	// IDM_ABOUTBOX должен быть в пределах системной команды.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Задает значок для этого диалогового окна. Среда делает это автоматически,
	//  если главное окно приложения не является диалоговым
	SetIcon(m_hIcon, TRUE);			// Крупный значок
	SetIcon(m_hIcon, FALSE);		// Мелкий значок

	// TODO: добавьте дополнительную инициализацию

	m_scanner_result.InsertColumn(0,_T("File"), 0, 200);
//	m_scanner_result.InsertColumn(1,_T("Mediainfo"),  0, 100);
//	m_scanner_result.InsertColumn(2,_T("Status"),  0, 30);
	return TRUE;  // возврат значения TRUE, если фокус не передан элементу управления
}

void CMediainfoscannerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// При добавлении кнопки свертывания в диалоговое окно нужно воспользоваться приведенным ниже кодом,
//  чтобы нарисовать значок. Для приложений MFC, использующих модель документов или представлений,
//  это автоматически выполняется рабочей областью.

void CMediainfoscannerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // контекст устройства для рисования

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Выравнивание значка по центру клиентского прямоугольника
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Нарисуйте значок
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// Система вызывает эту функцию для получения отображения курсора при перемещении
//  свернутого окна.
HCURSOR CMediainfoscannerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

static void ProcessMessages()
{
  CWinApp* pApp = AfxGetApp();
  MSG msg = {0};
  while ( PeekMessage ( &msg, NULL, 0, 0, PM_NOREMOVE ))
      pApp->PumpMessage();
}

size_t CMediainfoscannerDlg::getDataFromInet(LPCWSTR agent, const DWORD frameBufferSize, string const& url, string& data, LONG timeOut /*=0*/)
{
	size_t totalBytesRead = 0;
	DWORD numberOfBytesRead = 0;
	DWORD numberOfBytesToRead = frameBufferSize;
	
	std::vector<char> pTempData(frameBufferSize + 1);
	pTempData[0] = 0;
	CInternetHandle hInternet(InternetOpen(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0));
	if (!hInternet)
		return 0;
	if (timeOut)
		InternetSetOption(hInternet, INTERNET_OPTION_CONNECT_TIMEOUT, &timeOut, sizeof(timeOut));
	CInternetHandle hURL(InternetOpenUrlA(hInternet, url.c_str(), NULL, 0, INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD, 1)); // https://www.box.net/shared/096fe84f3e7e68666a3e
	// 2012-04-29_13-38-26_QSBM3KCUOSEPDSQX3BQ6IG7MTJ3QRPGV62TS46I_F9A54D9E_crash-stack-r501-build-9869.dmp
	if (!hURL)
	{
		// LogManager::getInstance()->message("InternetOpenUrl error = " + Util::translateError(GetLastError()));
		return 0;
	}
	bool isUserCancel = false;
	while (InternetReadFile(hURL, pTempData.data(), numberOfBytesToRead, &numberOfBytesRead))
	{
		if (numberOfBytesRead == 0)
		{
			// EOF detected
			break;
		}
		else
		{
			// Copy partial data
			pTempData[numberOfBytesRead] = '\0';
			data += pTempData.data();
			totalBytesRead += numberOfBytesRead;
		}
	}
	if (isUserCancel)
	{
		data = "";
		totalBytesRead = 0;
	}
	return totalBytesRead;
}
//======================================================================================================
//	mediainfo_ext ="3gp,avi,divx,flv,m4v,mkv,dts,mov,mp4,mpg,mpeg,vob,wmv,bik,qt,rm,aac,ac3,ape,fla,flac,m4a,mp1,mp2,mp3,ogg,wma,wv,mka,vqf,lqt,ts,tp,mod,m2ts,webm" 
static const wchar_t* g_mediainfo_ext[] =
{
	L"3gp",
	L"avi",
	L"divx",
	L"flv",
	L"m4v",
	L"mkv",
	L"dts",
	L"mov",
	L"mp4",
	L"mpg",
	L"mpeg",
	L"vob",
	L"wmv",
	L"bik",
	L"qt",
	L"rm",
	L"aac",
	L"ac3",
	L"ape",
	L"fla",
	L"flac",
	L"m4a",
	L"mp1",
	L"mp2",
	L"mp3",
	L"ogg",
	L"wma",
	L"wv",
	L"mka",
	L"vqf",
	L"lqt",
	L"ts",
	L"tp",
	L"mod",
	L"m2ts",
	L"webm",
#if defined(USE_MEDIAINFO_IMAGES) // Картинки не анализируем
	L"tiff",
	L"tif",
	L"jpg",
	L"ico",
	L"jpeg",
	L"gif",
	L"bmp",
	L"png",
	L"pcx",
	L"tga",
#endif
	0 // Не убирать терминатор!
};
//======================================================================================================
static std::wstring getFileExt(const std::wstring& p_path)
{
	std::wstring::size_type i = p_path.rfind(L'.');
	return (i != std::wstring::npos) ? p_path.substr(i+1) : L"";
}
//======================================================================================================
void CMediainfoscannerDlg::initOfflineConfig()
{
	if (m_mediainfo_ext.empty())
	{
	for (size_t i = 0; g_mediainfo_ext[i]; ++i)
		m_mediainfo_ext.insert(g_mediainfo_ext[i]);
	}
}
//======================================================================================================
void CMediainfoscannerDlg::load_config()
{
	initOfflineConfig();
#if 0
	 std::string l_data;
//#ifdef _DEBUG
	const string l_url_config_file = "file://C:/vc10/etc/flylinkdc-config.xml"; //TODO
//#else
//	const string l_url_config_file = "http://flylinkdc-update.googlecode.com/svn/trunk/etc/flylinkdc-config.xml";
//#endif
	if (getDataFromInet(_T("flylinkdc-config.xml"), 4096, l_url_config_file, l_data, 0) == 0)
	{
		initOfflineConfig();
	}
	else
	{
		try
		{
			SimpleXML l_xml;
			l_xml.fromXML(l_data);
			if (l_xml.findChild("Bootstraps"))
			{
				if(g_dht_servers.empty()) // DHT забираем один раз
				{
				 l_xml.stepIn();
				 while (l_xml.findChild("server"))
				 {
					const string& l_server = l_xml.getChildAttrib("url");
					const string& l_agent = l_xml.getChildAttrib("agent");
					if (!l_server.empty())
						g_dht_servers.push_back(DHTServer(l_server, l_agent));
				 }
				 l_xml.stepOut();
				}
				l_xml.stepIn();
				while (l_xml.findChild("fly-server"))
				{
					g_server_ip = l_xml.getChildAttrib("ip");
					g_server_port = Util::toInt(l_xml.getChildAttrib("port"));
					m_min_file_size = Util::toInt64(l_xml.getChildAttrib("min_file_size"));
					m_max_size_value = Util::toInt(l_xml.getChildAttrib("max_size_value"));
					m_zlib_compress_level = Util::toInt(l_xml.getChildAttrib("zlib_compress_level"));
					dcassert(m_zlib_compress_level >= 0 && m_zlib_compress_level < 10);
					if(m_zlib_compress_level <= 0 || m_zlib_compress_level > Z_BEST_COMPRESSION)
                                            m_zlib_compress_level = Z_BEST_COMPRESSION;
					m_send_full_mediainfo = Util::toInt(l_xml.getChildAttrib("send_full_mediainfo")) == 1;
					m_type = l_xml.getChildAttrib("type") == "http" ? TYPE_FLYSERVER_HTTP : TYPE_FLYSERVER_TCP ;
					// TODO - убрать копипаст и все токенайзеры унести в сервисный метод.

					const StringTokenizer<string> l_scan(l_xml.getChildAttrib("scan"), ',');
					m_scan.clear();
					for (auto i = l_scan.getTokens().cbegin(); i != l_scan.getTokens().cend(); ++i)
						{
							checkStrKey(*i);
							m_scan.insert(*i);
					    }
					
					const StringTokenizer<string> l_mediainfo_ext(l_xml.getChildAttrib("mediainfo_ext"), ',');
					m_mediainfo_ext.clear();
					for (auto i = l_mediainfo_ext.getTokens().cbegin(); i != l_mediainfo_ext.getTokens().cend(); ++i)
						{
							checkStrKey(*i);
							m_mediainfo_ext.insert(*i);
					    }

					const StringTokenizer<string> l_exclude_tag(l_xml.getChildAttrib("exclude_tag"), ',');
					m_exclude_tag.clear();
					for (auto i = l_exclude_tag.getTokens().cbegin(); i != l_exclude_tag.getTokens().cend(); ++i)
						m_exclude_tag.insert(*i);

					m_exclude_tag_inform.clear();
					const StringTokenizer<string> l_exclude_tag_inform(l_xml.getChildAttrib("exclude_tag_inform"), ',');
					for (auto i = l_exclude_tag_inform.getTokens().cbegin(); i != l_exclude_tag_inform.getTokens().cend(); ++i)
						m_exclude_tag_inform.push_back(*i);

					const StringTokenizer<string> l_include_tag(l_xml.getChildAttrib("include_tag"), ',');
					m_include_tag.clear();
					for (auto i = l_include_tag.getTokens().cbegin(); i != l_include_tag.getTokens().cend(); ++i)
						m_include_tag.insert(*i);
					
					const StringTokenizer<string> l_parasitic_file(l_xml.getChildAttrib("parasitic_file"), ',');
					g_parasitic_files.clear();
					for (auto i = l_parasitic_file.getTokens().cbegin(); i != l_parasitic_file.getTokens().cend(); ++i)
						g_parasitic_files.insert(*i);

					// Достанем RO-зеркала
					const StringTokenizer<string> l_mirror_read_only_server_tag(l_xml.getChildAttrib("mirror_read_only_server"), ',');
					g_mirror_read_only_servers.clear();
					for (auto i = l_mirror_read_only_server_tag.getTokens().cbegin(); i != l_mirror_read_only_server_tag.getTokens().cend(); ++i)
						{
							std::pair<std::string, uint16_t> l_ro_server_item;
							const auto l_port = i->find(':');
							if(l_port != string::npos)
							{
								l_ro_server_item.first = i->substr(0,l_port);
								l_ro_server_item.second = atoi(i->c_str()+l_port+1);
								g_mirror_read_only_servers.push_back(l_ro_server_item);
							}
					   }					
				}
				l_xml.stepOut();
			}
			l_dht_server_log.step("Download and parse - Ok!");
		}
		catch (const Exception& e)
		{
			dcassert(0);
			dcdebug("CFlyServerConfig::loadConfig parseXML ::Problem: %s\n", e.getError().c_str());
			l_dht_server_log.step("parseXML Problem:" + e.getError());
			initOfflineConfig();
		}
	}
	}
#endif // 0
}

void CMediainfoscannerDlg::mediainfo_scan(CString p_mask)
{
	load_config();
    static MediaInfoLib::MediaInfo g_media_info_lib;
    bool bFindEnd = false;
	WIN32_FIND_DATA FindData = {0};
	CString l_mask = p_mask + "\\*.*";
    HANDLE handle = FindFirstFile(l_mask, &FindData);
	int l_item = 0;
    if(handle != INVALID_HANDLE_VALUE)
      {
	  while(!bFindEnd)
      {
		if(!(FindData.cFileName[0] == L'.' && FindData.cFileName[1] == 0 ||
		     FindData.cFileName[0] == L'.' && FindData.cFileName[1] == L'.' && FindData.cFileName[2] == 0))
	   {
        const CString l_file = p_mask + L"\\" + FindData.cFileName;
		if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			ProcessMessages();
			mediainfo_scan(l_file);
		}
		const std::wstring l_ext = getFileExt(FindData.cFileName);
		CString l_caption = L"FlylinkDC++ MediaInfo Scanner v.1.0.0.1 -  ";
		l_caption += l_file;
		SetWindowText(l_caption);
		if(m_mediainfo_ext.find(l_ext) != m_mediainfo_ext.end())
		{
		if (g_media_info_lib.Open(FindData.cFileName))
		{
		}
		m_scanner_result.InsertItem(l_item++,l_file);
		}
	   }
	   
       bFindEnd = !FindNextFile(handle, &FindData);
      }
      FindClose(handle);
	}
}

void CMediainfoscannerDlg::OnBnClickedStartScannerButton()
{
	m_start_scanner.EnableWindow(FALSE);
	m_scanner_result.DeleteAllItems();
	mediainfo_scan(m_Path);
	m_start_scanner.EnableWindow(TRUE);
//     static MediaInfoLib::MediaInfo g_media_info_lib;
/*
const string l_file_ext = Text::toLower(Util::getFileExtWithoutDot(p_name));
		const bool l_is_media_info_fly_server = g_fly_server_config.isSupportFile(l_file_ext, p_size); // TODO яЁютхЁъє юсюс∙шЄ№ т ъырёёх ъюэЇшуєЁрЄюЁр
		if (g_fly_server_config.m_mediainfo_ext.find(l_file_ext) == g_fly_server_config.m_mediainfo_ext.end() && l_is_media_info_fly_server == false) // [!] IRainman opt.
			return false;
		char l_size[64];
		l_size[0] = 0;
		_snprintf(l_size, sizeof(l_size), "%I64d", p_size);
		g_cur_mediainfo_file = p_name + "\r\nTTH = " + p_tth.toBase32() + "\r\nFile size = " + string(l_size);
*/
  

}


void CMediainfoscannerDlg::OnTreeSelChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
	
	if(m_filetree.GetItemPath(m_Path))
	{
		m_scanner_path.SetWindowTextW(m_Path);
	}
	// TODO: Add your control notification handler code here
	*pResult = 0;
}
